#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <netdb.h>
#include "url.h"
#include "net.h"
#include "gfw.h"

gfw_request *gfw_init() {
    gfw_request *gr;
    gr=malloc(sizeof(gfw_request));
    gr->url=NULL;
    gr->method="GET";
    gr->header=NULL;
    gr->cookie=NULL;
    gr->post_data=NULL;
    gr->http_version="1.0";
    return gr;
}

bool gfw_set_version(gfw_request *gr,char *version) {
    if (!version || strcmp(version,"1.0")!=0 && strcmp(version,"1.1")!=0) {
        return false;
    }
    gr->http_version=malloc(strlen(version)+1);
    strcpy(gr->http_version,version);
    return true;
}
bool gfw_set_url(gfw_request *gr,char *url) {
    gr->url=malloc(strlen(url)+1);
    strcpy(gr->url,url);
    return true;
}
bool gfw_set_method(gfw_request *gr,char *method) {
    if (!method || strcmp(method,"POST")!=0 && strcmp(method,"GET")!=0) {
        return false;
    }
    gr->method=malloc(strlen(method)+1);
    strcpy(gr->method,method);
    return true;
}
bool gfw_add_cookie(gfw_request *gr,char *key,char *value) {
    gr->cookie=kv_remove(gr->cookie,key);
    gr->cookie=kv_add(gr->cookie,key,value);
    return true;
}
bool gfw_add_header(gfw_request *gr,char *key,char *value) {
    gr->header=kv_add(gr->header,key,value);
    return true;
}
bool gfw_add_post_data(gfw_request *gr,char *key,char *value) {
    gr->post_data=kv_add(gr->post_data,key,gfw_url_encode(value));
    return true;
}
bool gfw_remove_header(gfw_request *gr,char *key) {
    gr->header=kv_remove(gr->header,key);
    return true;
}
bool gfw_add_unique_header(gfw_request *gr,char *key,char *value) {
    gfw_remove_header(gr,key);
    gfw_add_header(gr,key,value);
    return true;
}
gfw_response *gfw_send(gfw_request *gr) {
    gfw_response *gres=NULL;
    uint16_t request_length=0;
    if (!gr->url) {
        return gres;
    }
    gfw_url *url;
    url=gfw_url_parse(gr->url);
    if (!url->host) {
        return gres;
    }
    if (!url->path) {
        url->path="/";
    }
    
    gfw_add_header(gr,"Host",url->host);
    if (!strcmp(gr->http_version,"1.1")) {
        gfw_add_header(gr,"Connection","close");
    }

    request_length+=strlen(url->path);
    if (url->query) {
        request_length+=1+strlen(url->query);
    }
    /*2 spaces + strlen(httpversion_string) + strlen(CRLF)=12*/
    request_length+=strlen(gr->method)+12;

    if (!strcmp(gr->method,"POST")) {
        gfw_add_unique_header(gr,"Content-Type", "application/x-www-form-urlencoded");
    }

    if (gr->cookie) {
        char *header_cookie=kv_join(gr->cookie,"=","; "); 
        if (header_cookie) {
            gfw_add_header(gr,"Cookie",header_cookie);
        }
    }

    char *post_string=NULL;
    if (!strcmp(gr->method,"POST") && gr->post_data) {
        post_string=kv_join(gr->post_data,"=","&"); 
        if (post_string) {
            char *clen;
            uint32_t post_str_len=strlen(post_string);
            uint16_t clen_len=post_str_len/10;
            if (post_str_len%10!=0) {
                clen_len++;
            }
            clen=malloc(clen_len+1);
            sprintf(clen,"%d",post_str_len);
            gfw_add_header(gr,"Content-Length",clen);
            request_length+=strlen(post_string);
        }
    }

    char *main_request=kv_join(gr->header,": ","\r\n");
    request_length+=strlen(main_request)+4;

    char *request_str=malloc(request_length);
    strcat(request_str,gr->method);
    strcat(request_str," ");
    strcat(request_str,url->path);
    if (url->query) {
        strcat(request_str,"?");
        strcat(request_str,url->query);
    }
    strcat(request_str," HTTP/");
    strcat(request_str,gr->http_version);
    strcat(request_str,"\r\n");
    strcat(request_str,main_request);
    strcat(request_str,"\r\n\r\n");
    if (post_string) {
        strcat(request_str,post_string);
    }
    struct hostent *host;
    struct in_addr in;
    host=gethostbyname(url->host);
    if (host==NULL || host->h_addr_list==NULL) {
        return gres;
    }
    if (url->port==0) {
        url->port=80;
    }
    in = *(struct in_addr *) host->h_addr_list[0];
    //char *ip=(char *)inet_ntoa(in);
    char *res=tcp_send(request_str,in.s_addr,url->port);
    gres=gfw_response_parse(res);
    free(res);

    gfw_remove_header(gr,"Content-Type");
    gfw_remove_header(gr,"Cookie");
    gfw_remove_header(gr,"Content-Length");
    if (!strcmp(gr->http_version,"1.1")) {
        gfw_remove_header(gr,"Connection");
    }
    free(main_request);
    free(request_str);
    return gres;
}
bool gfw_free(gfw_request *gr) {
    free(gr->url);
    free(gr->method);
    free(gr->http_version);
    kv_free(gr->header);
    kv_free(gr->cookie);
    kv_free(gr->post_data);
    free(gr);
    return true;
}
